package com.gsbelarus.gedemin.salary.database;

import android.util.Log;

import com.gsbelarus.gedemin.lib.Utils;
import com.gsbelarus.gedemin.lib.ui.activity.drawer.adapter.table.DrawerProfile;
import com.gsbelarus.gedemin.salary.entity.StatisticPayslip;
import com.gsbelarus.gedemin.salary.entity.model.DayEventModel;
import com.gsbelarus.gedemin.salary.entity.model.EmployeeModel;
import com.gsbelarus.gedemin.salary.entity.model.ExRatesMonthlyModel;
import com.gsbelarus.gedemin.salary.entity.model.GdMsgEmployeeModel;
import com.gsbelarus.gedemin.salary.entity.model.GdMsgHolidayModel;
import com.gsbelarus.gedemin.salary.entity.model.GdMsgIncomeModel;
import com.gsbelarus.gedemin.salary.entity.model.GdMsgModel;
import com.gsbelarus.gedemin.salary.entity.model.GdMsgVacationModel;
import com.gsbelarus.gedemin.salary.entity.model.GdMsgWorkScheduleModel;
import com.gsbelarus.gedemin.salary.entity.model.PayslipBenefitModel;
import com.gsbelarus.gedemin.salary.entity.model.PayslipDeductionModel;
import com.gsbelarus.gedemin.salary.entity.model.PayslipItemModel;
import com.gsbelarus.gedemin.salary.entity.model.PayslipModel;
import com.gsbelarus.gedemin.salary.entity.model.SyncGdMsgStateModel;
import com.gsbelarus.gedemin.salary.entity.model.SyncInfoModel;
import com.gsbelarus.gedemin.salary.entity.model.TimesheetModel;
import com.gsbelarus.gedemin.salary.entity.model.WorkScheduleModel;
import com.gsbelarus.gedemin.salary.util.DateFormatHelper;
import com.gsbelarus.gedemin.salary.util.ModelFactory;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Random;

import io.realm.Realm;
import io.realm.RealmObject;
import io.realm.RealmResults;
import io.realm.exceptions.RealmException;

public class RealmHelper {

    private Realm realm;

    public RealmHelper(Realm realm) {
        this.realm = realm;
    }

    public Realm getRealm() {
        return realm;
    }

    public void clearDB(boolean withTransaction) {
        if (withTransaction)
            realm.beginTransaction();

        realm.clear(SyncInfoModel.class);
        realm.clear(SyncGdMsgStateModel.class);

        realm.clear(EmployeeModel.class);
        realm.clear(PayslipModel.class);
        realm.clear(PayslipBenefitModel.class);
        realm.clear(PayslipDeductionModel.class);
        realm.clear(PayslipItemModel.class);

        realm.clear(DayEventModel.class);
        realm.clear(WorkScheduleModel.class);
        realm.clear(ExRatesMonthlyModel.class);

        realm.clear(GdMsgModel.class);
        realm.clear(GdMsgEmployeeModel.class);
        realm.clear(GdMsgHolidayModel.class);
        realm.clear(GdMsgIncomeModel.class);
        realm.clear(GdMsgVacationModel.class);
        realm.clear(GdMsgWorkScheduleModel.class);

        realm.clear(TimesheetModel.class);

        if (withTransaction)
            realm.commitTransaction();
    }

    public long randLong(long min, long max) {
        long result;
        do {
            result = (new Random().nextLong() % (max - min)) + min;
        } while (result <= 0);

        return result;
    }

    public int randInt(int min, int max, Random rand) {
        return rand.nextInt((max - min) + 1) + min;
    }

    public <T extends RealmObject> boolean existOnUid(T item, int uid) {
        return realm.where(item.getClass()).equalTo("uid", uid).count() != 0;
    }

    public <T extends RealmObject> T save(T item, boolean withTransaction) {
        T realmItem;
        try {
            if (withTransaction)
                realm.beginTransaction();
            realmItem = realm.copyToRealmOrUpdate(item);

            if (withTransaction)
                realm.commitTransaction();
        } catch (RealmException ex) {
            if (withTransaction)
                realm.cancelTransaction();
            return null;
        }

        return realmItem;
    }

    public void saveObjectList(List<? extends RealmObject> realmObjects, boolean withTransaction) {
        if (withTransaction)
            realm.beginTransaction();

        for (RealmObject realmObject : realmObjects)
            if (realmObject != null)
                realm.copyToRealmOrUpdate(realmObject);

        if (withTransaction)
            realm.commitTransaction();
    }

    public void deleteObject(boolean withTransaction, RealmObject... realmObjects) {
        try {
            if (withTransaction)
                realm.beginTransaction();

            for (RealmObject realmObject : realmObjects)
                if (realmObject != null)
                    realmObject.removeFromRealm();

            if (withTransaction)
                realm.commitTransaction();
        } catch (Exception ex) {
            Log.e("my", "deleteObject " + ex.getMessage());
            if (withTransaction)
                realm.cancelTransaction();
        }
    }

    public void deleteObjectList(List<? extends RealmObject> realmObjects, boolean withTransaction) {
        try {
            if (withTransaction)
                realm.beginTransaction();

            ///for (RealmObject realmObject : realmObjects)
            ///    if (realmObject != null)
            ///        realmObject.removeFromRealm();
            for (int i = realmObjects.size() - 1; i >= 0; i--)
                if (realmObjects.get(i) != null)
                    realmObjects.get(i).removeFromRealm();

            if (withTransaction)
                realm.commitTransaction();
        } catch (Exception ex) {
            Log.e("my", "deleteObjectList " + ex.getMessage());
            if (withTransaction)
                realm.cancelTransaction();
        }
    }

    public <T extends RealmObject> void deleteAll(Class<T> clazz, boolean withTransaction) {
        if (withTransaction)
            realm.beginTransaction();

        realm.clear(clazz);

        if (withTransaction)
            realm.commitTransaction();
    }

    //getters

    public <T extends RealmObject> RealmResults<T> getAll(Class<T> clazz) {
        return realm.where(clazz).findAll();
    }

    public EmployeeModel getLastEmployee() {
        PayslipModel payslipModel = getLastPayslip();
        if (payslipModel == null) return null;
        return payslipModel.getEmployee();
    }

    // рабочий график на месяц
    public RealmResults<WorkScheduleModel> getMonthWorkSchedule(Calendar date) {
        Date[] monthPeriod = DateFormatHelper.getMonthPeriodDates(date);
        return realm.where(WorkScheduleModel.class)
                .between("date", monthPeriod[0], monthPeriod[1])
                .findAllSorted("date");
    }

    public PayslipModel getLastPayslip() {
        try {
            return realm.where(PayslipModel.class)
                    .findAllSorted("payDate", false)
                    .first();
        } catch (RuntimeException e) {
            //Crashlytics.getInstance().core.logException(e);
            Log.d("getLastPayslip", e.toString());
        }
        return null;
    }

    public PayslipModel getPayslipByUID(String uid) {
        try {
            return realm.where(PayslipModel.class)
                    .contains("uid", uid)
                    .findFirst();
        } catch (RuntimeException e) {
            Log.d("getPayslipByUID", e.toString());
        }
        return null;
    }

    public EmployeeModel getEmployeeByUID(String key) {
        try {
            return realm.where(EmployeeModel.class)
                    .contains("uid", key).findFirst();
        } catch (RuntimeException e) {
            Log.d("getEmployeeByUID", e.toString());
        }
        return null;
    }

    public WorkScheduleModel getWorkScheduleByUID(String key) {
        try {
            return realm.where(WorkScheduleModel.class)
                    .contains("uid", key).findFirst();
        } catch (RuntimeException e) {
            Log.d("getWorkScheduleByUID", e.toString());
        }
        return null;
    }

    public WorkScheduleModel getWorkScheduleByDay(Date date) {
        try {
            Calendar begin = Calendar.getInstance();
            begin.setTime(date);
            Calendar end = (Calendar) begin.clone();
            begin.set(Calendar.HOUR_OF_DAY, 0);
            end.set(Calendar.HOUR_OF_DAY, 23);

            begin.set(Calendar.MINUTE, 0);
            end.set(Calendar.MINUTE, 59);

            begin.set(Calendar.SECOND, 0);
            end.set(Calendar.SECOND, 59);
            return realm.where(WorkScheduleModel.class)
                    .between("date", begin.getTime(), end.getTime()).findFirst();
        } catch (RuntimeException e) {
            //Crashlytics.getInstance().core.logException(e);
            Log.d("getWorkScheduleByDay", e.toString());
        }
        return null;
    }

    public GdMsgModel getGdMsgByUID(int key) {
        try {
            return realm.where(GdMsgModel.class)
                    .equalTo("uid", key).findFirst();
        } catch (RuntimeException e) {
            Log.d("getWorkScheduleByUID", e.toString());
        }
        return null;
    }

    public RealmResults<PayslipItemModel> getItemsByPayslipKey(String key) {
        try {
            return realm.where(PayslipItemModel.class)
                    .contains("payslipKey", key).findAll();
        } catch (RuntimeException e) {
            //Crashlytics.getInstance().core.logException(e);
            Log.d("getItemsByPayslipKey", e.toString());
        }
        return null;
    }

    public RealmResults<PayslipBenefitModel> getBenefitsByPayslipKey(String key) {
        try {
            return realm.where(PayslipBenefitModel.class)
                    .contains("payslipKey", key).findAll();
        } catch (RuntimeException e) {
            Log.d("getBenefitsByPayslipKey", e.toString());
        }
        return null;
    }

    public RealmResults<PayslipDeductionModel> getDeductionsByPayslipKey(String key) {
        try {
            return realm.where(PayslipDeductionModel.class)
                    .contains("payslipKey", key).findAll();
        } catch (RuntimeException e) {
            Log.d("getDeductByPayslipKey", e.toString());
        }
        return null;
    }

    public RealmResults<DayEventModel> getEventsByWorkScheduleKey(String key) {
        try {
            return realm.where(DayEventModel.class)
                    .contains("events", key).findAll();
        } catch (RuntimeException e) {
            Log.d("getEvsByWorkScheduleKey", e.toString());
        }
        return null;
    }


    //TODO переделать

    /**
     * Данные для элемента profile в Drawer-е
     * Содержит "Имя Фамилия" и "описание"
     */
    public DrawerProfile getDrawerProfile() {
        PayslipModel lastPayslipModel = getLastPayslip();

        DrawerProfile profile = null;
        if (lastPayslipModel != null) {
            profile = new DrawerProfile();
            profile.setText(lastPayslipModel.getEmployee().getFirstname() + " " + lastPayslipModel.getEmployee().getSurname());
            profile.setSummary(lastPayslipModel.getPosition());
        }

        return profile;
    }

    /**
     * Курс валют на месяц
     */
    public ExRatesMonthlyModel getExRatesMonthly(Calendar date) {
        Date[] monthPeriod = DateFormatHelper.getMonthPeriodDates(date);

        try {
            return realm.where(ExRatesMonthlyModel.class)
                    .between("onDate", monthPeriod[0], monthPeriod[1])
                    .findFirst();
        } catch (RuntimeException e) {
            //Crashlytics.getInstance().core.logException(e);
            Log.d("getExRatesMonthly", e.toString());
        }
        return null;
    }

    /**
     * Даты присутствующие в листках
     */
    public List<Calendar> getPayslipPossibleDatesList() {
        //TODO обработать когда несколько листков на один месяц
        RealmResults<PayslipModel> payslips = realm.where(PayslipModel.class)
                .findAllSorted("payDate", true);

        List<Calendar> list = new ArrayList<>();
        if (payslips.isEmpty()) {
            list.add(Calendar.getInstance());
        } else {
            for (PayslipModel payslip : payslips) {
                Calendar cal = Calendar.getInstance();
                cal.setTime(payslip.getPayDate());
                list.add(cal);
            }
        }

        return list;
    }

    /**
     * Листок за месяц
     */
    public PayslipModel getPayslip(Calendar date) {
        Date[] monthPeriod = DateFormatHelper.getMonthPeriodDates(date);

        try {
            return realm.where(PayslipModel.class)
                    .equalTo("employee.uid", getLastEmployee().getUid())
                    .between("payDate", monthPeriod[0], monthPeriod[1])
                    .findAllSorted("payDate", false)
                    .first();
        } catch (RuntimeException e) {
            Log.d("getPayslip", e.toString());
        }
        return null;
    }

    public List<StatisticPayslip> getPayslipStatistics(Calendar firstDate, Calendar secondDate) {   //TODO  return RealmResult
        List<StatisticPayslip> payslips = new ArrayList<>();

        Date beginPeriodDate = DateFormatHelper.getMonthPeriodDates(firstDate)[0];
        Date endPeriodDate = DateFormatHelper.getMonthPeriodDates(secondDate)[1];

        try {
            RealmResults<PayslipModel> result = realm.where(PayslipModel.class)
                    .equalTo("employee.uid", getLastEmployee().getUid())
                    .between("payDate", beginPeriodDate, endPeriodDate)
                    .findAllSorted("payDate", true);

            for (PayslipModel item : result) {

                double total = item.getItems().where().sumDouble("debit") - item.getItems().where()
                        .notEqualTo("categoryIndex", PayslipItemModel.Category.PREPAYMENT.ordinal())
                        .sumDouble("credit");
                Calendar cal = Calendar.getInstance();
                cal.setTime(item.getPayDate());
                payslips.add(new StatisticPayslip(
                        cal,
                        total));
            }
        } catch (RuntimeException e) {
            //Crashlytics.getInstance().core.logException(e);
            Log.d("getPayslipStatistics", e.toString());
        }
        return payslips;
    }

    public Double getTotalSumWithPrepayment(Calendar cal) {
        Date beginPeriodDate = DateFormatHelper.getMonthPeriodDates(cal)[0];
        Date endPeriodDate = DateFormatHelper.getMonthPeriodDates(cal)[1];

        EmployeeModel employeeModel = getLastEmployee();
        if (employeeModel == null) return null;

        PayslipModel result = realm.where(PayslipModel.class)
                .equalTo("employee.uid", employeeModel.getUid())
                .between("payDate", beginPeriodDate, endPeriodDate)
                .findFirst();

        if (result == null) return null;

        return result.getItems().where().sumDouble("debit") - result.getItems().where()
                .notEqualTo("categoryIndex", PayslipItemModel.Category.PREPAYMENT.ordinal())
                .sumDouble("credit");
    }

    /**
     * общая сумма Аванса для листка
     */
    public double getPrepaymentSum(PayslipModel payslipModel) {
        try {
            RealmResults<PayslipItemModel> prepayments = payslipModel.getItems().where()
                    .equalTo("categoryIndex", PayslipItemModel.Category.PREPAYMENT.ordinal())
                            // .equalTo("debit", 0.0)
                    .findAll();

            return prepayments.sum("credit").doubleValue();
        } catch (RuntimeException e) {
            //Crashlytics.getInstance().core.logException(e);
            Log.d("getPrepaymentSum", e.toString());
        }
        return 0;
    }

    /**
     * Cписок налогов для листка
     */
    public RealmResults<PayslipItemModel> getTaxesList(PayslipModel payslipModel) { //TODO group by CODE
        return payslipModel.getItems().where()
                .equalTo("categoryIndex", PayslipItemModel.Category.TAX.ordinal())
                        // .equalTo("debit", 0.0)
                .findAllSorted("credit", false);
    }

    /**
     * Список yдержаний для листка  (кроме налогов и аванса).
     */
    public RealmResults<PayslipItemModel> getCreditList(PayslipModel payslipModel) { //TODO group by CODE
        return payslipModel.getItems().where()
                .equalTo("categoryIndex", PayslipItemModel.Category.CREDIT.ordinal())
                        //  .equalTo("debit", 0.0)
                .findAllSorted("credit", false);
    }

    //Список начислений для листка
    public RealmResults<PayslipItemModel> getDebitList(PayslipModel payslipModel) { //TODO group by CODE
        return payslipModel.getItems().where()
                .equalTo("categoryIndex", PayslipItemModel.Category.DEBIT.ordinal())
                        // .equalTo("credit", 0.0)
                .findAllSorted("debit", false);
    }


    // кол-во непрочитанных сообщений
    public long getUnreadGdMsgsCount() {
        return realm.where(GdMsgModel.class).equalTo("isUnread", true).count();
    }

    // последнее сообщение
    public GdMsgModel getLastGdMsg() {
        return realm.where(GdMsgModel.class).findAllSorted("timestamp", false).first();
    }

    // все сообщения //TODO limit
    public RealmResults<GdMsgModel> getAllGdMsgs() {
        return realm.allObjectsSorted(GdMsgModel.class, "timestamp", false);
    }

    // сообщение прочитано
    public void setGdMsgViewedWithSync(GdMsgModel gdMsgModel) {
        try {
            realm.beginTransaction();
            gdMsgModel.setIsUnread(false);
            ModelFactory.newInstanceSyncGdMsgStatusModel(realm.createObject(SyncGdMsgStateModel.class), SyncGdMsgStateModel.UpdatedStatus.READED, gdMsgModel.getUid());
            realm.commitTransaction();

        } catch (IllegalStateException e) {
            gdMsgModel.setIsUnread(false);
            ModelFactory.newInstanceSyncGdMsgStatusModel(realm.createObject(SyncGdMsgStateModel.class), SyncGdMsgStateModel.UpdatedStatus.READED, gdMsgModel.getUid());
        }
    }

    public void deleteGdMsgWithSync(GdMsgModel gdMsgModel, boolean withTransaction) {
        //TODO assertNotNull(gdMsgModel);
        save(ModelFactory.newInstanceSyncGdMsgStatusModel(new SyncGdMsgStateModel(), SyncGdMsgStateModel.UpdatedStatus.DELETED, gdMsgModel.getUid()), withTransaction);
        deleteObject(withTransaction, gdMsgModel);
    }

    public SyncInfoModel getSyncInfo(boolean withTransaction) {
        SyncInfoModel syncInfoModel = realm.where(SyncInfoModel.class).findFirst();
        if (syncInfoModel == null) {
            if (withTransaction)
                realm.beginTransaction();
            String defaultDate = "01.01.1900 00:00:00";
            syncInfoModel = realm.copyToRealm(new SyncInfoModel());
            syncInfoModel.setLastSyncDate(defaultDate);
            syncInfoModel.setLastSyncMsgDate(defaultDate);
            if (withTransaction)
                realm.commitTransaction();
        }
        return syncInfoModel;
    }

    public int generateUID(Class cl) {
        try {
            return realm.where(cl).findAll().max("uid").intValue() + 1;
        } catch (RuntimeException e) {
            Log.d("generateUID", e.toString());
        }
        return 1;
    }

    public void applyGdMsgState(SyncGdMsgStateModel state, boolean withTransaction) {
        if (withTransaction)
            realm.beginTransaction();

        GdMsgModel gdMsg = getGdMsgByUID(state.getGdMsgKey());
        if (gdMsg != null) {
            switch (state.getStatus()) {
                case READED:
                    gdMsg.setIsUnread(false);
                    break;
                case DELETED:
                    gdMsg.removeFromRealm();
                    break;
            }
        }

        RealmResults<SyncGdMsgStateModel> gdMsgStates = realm.where(SyncGdMsgStateModel.class)
                .equalTo("gdMsgKey", state.getGdMsgKey())
                .equalTo("statusIndex", state.getStatusIndex()).findAll();

        deleteObjectList(gdMsgStates, false);

        if (withTransaction)
            realm.commitTransaction();
    }

    public void initializeDemoData() {
        SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
        SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("dd.MM.yyyy HH:mm");

        // Пользователь
        realm.beginTransaction();

        EmployeeModel employeeModel = ModelFactory.newInstanceEmployeeModel(
                realm.createObject(EmployeeModel.class),
                "Владимир", "Шестак", "Владимирович", "3230969K026PB0", "2902");

        realm.commitTransaction();

        // Рабочий график
        realm.beginTransaction();

        try {
            List<Date> holidayDates = new ArrayList<>();
            holidayDates.add(dateFormatter.parse("01.01.2015"));
            holidayDates.add(dateFormatter.parse("07.01.2015"));
            holidayDates.add(dateFormatter.parse("23.02.2015"));
            holidayDates.add(dateFormatter.parse("08.03.2015"));
            holidayDates.add(dateFormatter.parse("15.03.2015"));
            holidayDates.add(dateFormatter.parse("21.04.2015"));
            holidayDates.add(dateFormatter.parse("26.04.2015"));
            holidayDates.add(dateFormatter.parse("01.05.2015"));
            holidayDates.add(dateFormatter.parse("09.05.2015"));
            holidayDates.add(dateFormatter.parse("10.05.2015"));
            holidayDates.add(dateFormatter.parse("03.07.2015"));
            holidayDates.add(dateFormatter.parse("07.11.2015"));
            holidayDates.add(dateFormatter.parse("25.12.2015"));

            List<String> holidayNames = new ArrayList<>();
            holidayNames.add("Новый Год");
            holidayNames.add("Православное Рождество");
            holidayNames.add("День Защитника Отечества");
            holidayNames.add("Международный Женский День");
            holidayNames.add("День Конституции");
            holidayNames.add("Радоница");
            holidayNames.add("День Памяти Чернобыльской Трагедии");
            holidayNames.add("День Труда");
            holidayNames.add("День Победы");
            holidayNames.add("День Национальной Символики");
            holidayNames.add("День Независимости Республики Беларусь");
            holidayNames.add("День Октябрьской Революции");
            holidayNames.add("Католическое Рождество");

            List<Date> transfersWorkDay = new ArrayList<>();  // рабочие выходные
            transfersWorkDay.add(dateFormatter.parse("10.01.2015")); // от 2
            transfersWorkDay.add(dateFormatter.parse("25.04.2015")); // от 20

            generateInstanseWorkSchedules(dateFormatter.parse("01.01.2015"), dateFormatter.parse("31.12.2015"), employeeModel,
                    holidayDates, transfersWorkDay, holidayNames);

        } catch (ParseException e) {
            e.printStackTrace();
        }

        realm.commitTransaction();

        // Курсы валют
        realm.beginTransaction();

        try {
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("06.04.2015"),
                    14620,
                    15910);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.03.2015"),
                    15060,
                    15950);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.02.2015"),
                    15090,
                    17240);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.01.2015"),
                    15010,
                    17710);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.12.2014"),
                    10860,
                    13450);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.11.2014"),
                    10760,
                    13390);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.10.2014"),
                    10650,
                    13520);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.09.2014"),
                    10490,
                    13560);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.08.2014"),
                    10370,
                    13860);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.07.2014"),
                    10250,
                    13970);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.06.2014"),
                    10150,
                    13770);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.05.2014"),
                    10020,
                    13740);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.04.2014"),
                    9930,
                    13760);
            ModelFactory.newInstanceExRatesMonthlyModel(
                    realm.createObject(ExRatesMonthlyModel.class),
                    dateFormatter.parse("15.03.2014"),
                    9820,
                    13610);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        realm.commitTransaction();

        // Расчетные листки
        realm.beginTransaction();

        Random rand = new Random();
        Calendar calMonth = Calendar.getInstance();
        Utils.initCalendarFields(calMonth, Calendar.MONTH, 1);

        for (int i = 0; i < 12; i++) {
            calMonth.add(Calendar.MONTH, -1);
            Calendar calDate = (Calendar) calMonth.clone();
            Date date = new Date(calDate.getTimeInMillis());

            /**
             realm.beginTransaction();

             for (int k = 0; k < calMonth.getActualMaximum(Calendar.DAY_OF_MONTH); k++) {
             TimesheetModel timesheetModel = ModelFactory.newInstanceTimesheetModel(
             realm.createObject(TimesheetModel.class),
             employeeModel,
             new Date(calDate.getTimeInMillis()));

             timesheetModel.getDayEvents().add(ModelFactory.newInstanceDayEventModel(
             realm.createObject(DayEventModel.class),
             timeBegin,
             timeEnd,
             description,
             isAllDay));

             calDate.add(Calendar.DATE, 1);
             }

             realm.commitTransaction();
             **/

            PayslipModel payslipModel = ModelFactory.newInstancePayslipModel(
                    realm.createObject(PayslipModel.class),
                    date,
                    employeeModel,
                    "Электроучасток",
                    "Электромантер",
                    0,
                    5312);
            // начисления
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.DEBIT,
                    date,
                    "Доплата за совмещение профессий (6 дн.)",
                    100,
                    (double) randInt(200000, 300000, rand),
                    0));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.DEBIT,
                    date,
                    "За работу в выходные",
                    101,
                    (double) randInt(300000, 400000, rand),
                    0));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.DEBIT,
                    date,
                    "Премия",
                    102,
                    (double) randInt(400000, 600000, rand),
                    0));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.DEBIT,
                    date,
                    "Доплата за выслугу лет",
                    103,
                    (double) randInt(200000, 300000, rand),
                    0));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.DEBIT,
                    date,
                    "Материальная помощь",
                    104,
                    (double) randInt(500000, 700000, rand),
                    0));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.DEBIT,
                    date,
                    "За работу в ночь (56 ч)",
                    104,
                    (double) randInt(2700000, 3500000, rand),
                    0));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.DEBIT,
                    date,
                    "Повременно (13 дн. 104 ч.)",
                    105,
                    (double) randInt(3000000, 4000000, rand),
                    0));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.DEBIT,
                    date,
                    "Питание",
                    106,
                    (double) randInt(150000, 300000, rand),
                    0));
            // удержания
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.CREDIT,
                    date,
                    "Долг за сотрудником",
                    200,
                    0,
                    (double) randInt(100000, 160000, rand)));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.CREDIT,
                    date,
                    "БАНК",
                    201,
                    0,
                    (double) randInt(400000, 800000, rand))); // 6 500 000
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.PREPAYMENT,
                    date,
                    "Аванс",
                    202,
                    0,
                    (double) randInt(1200000, 1500000, rand)));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.CREDIT,
                    date,
                    "Удержание за мобильную связь",
                    203,
                    0,
                    (double) randInt(15000, 30000, rand)));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.CREDIT,
                    date,
                    "Субботник",
                    204,
                    0,
                    (double) randInt(60000, 90000, rand)));
            // налоги
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.TAX,
                    date,
                    "Подоходный",
                    205,
                    0,
                    (double) randInt(500000, 750000, rand)));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.TAX,
                    date,
                    "Профсоюзный",
                    206,
                    0,
                    (double) randInt(60000, 90000, rand)));
            payslipModel.getItems().add(ModelFactory.newInstancePayslipItemModel(
                    realm.createObject(PayslipItemModel.class),
                    PayslipItemModel.Category.TAX,
                    date,
                    "Пенсионный", //60000
                    207,
                    0,
                    (double) randInt(50000, 75000, rand)));
            // льготы
            payslipModel.getBenefits().add(ModelFactory.newInstancePayslipBenefitModel(
                    realm.createObject(PayslipBenefitModel.class),
                    date,
                    "Вознаграждение",  //518
                    (double) randInt(200000, 400000, rand)));
            payslipModel.getBenefits().add(ModelFactory.newInstancePayslipBenefitModel(
                    realm.createObject(PayslipBenefitModel.class),
                    date,
                    "Cтоимость путевок",  //509
                    (double) randInt(300000, 500000, rand)));
            // вычеты
            payslipModel.getDeductions().add(ModelFactory.newInstancePayslipDeductionModel(
                    realm.createObject(PayslipDeductionModel.class),
                    date,
                    "На строительство жилья", //650
                    "Стандартный вычет",
                    (double) randInt(250000, 350000, rand)));
            payslipModel.getDeductions().add(ModelFactory.newInstancePayslipDeductionModel(
                    realm.createObject(PayslipDeductionModel.class),
                    date,
                    "Cумма, уплаченная за обучение", //640
                    "Стандартный вычет",
                    (double) randInt(170000, 250000, rand)));
            payslipModel.getDeductions().add(ModelFactory.newInstancePayslipDeductionModel(
                    realm.createObject(PayslipDeductionModel.class),
                    date,
                    "На ребенка", //610
                    "Стандартный вычет",
                    (double) randInt(300000, 400000, rand)));
        }
        realm.commitTransaction();

        //Cообщения
        realm.beginTransaction();

        try {
            //14
            GdMsgModel gdMsgHolidayModel = ModelFactory.newInstanceGdMsgHolidayModel(generateUID(GdMsgHolidayModel.class), realm.createObject(GdMsgHolidayModel.class),
                    generateUID(GdMsgModel.class), realm.createObject(GdMsgModel.class), "", dateTimeFormatter.parse("12.06.2014 12:17"), dateTimeFormatter.parse("02.07.2014 00:00"), dateFormatter.parse("07.07.2014 00:00"), GdMsgHolidayModel.Type.VACATION);

            ModelFactory.newInstanceGdMsgHolidayModel(generateUID(GdMsgHolidayModel.class), realm.createObject(GdMsgHolidayModel.class),
                    generateUID(GdMsgModel.class), realm.createObject(GdMsgModel.class), "", "", dateTimeFormatter.parse("12.07.2014 13:45"),
                    GdMsgModel.RequestStatus.REJECTED,
                    gdMsgHolidayModel
            );
            //16
            GdMsgModel gdMsgIncomeModel = ModelFactory.newInstanceGdMsgIncomeModel(generateUID(GdMsgIncomeModel.class), realm.createObject(GdMsgIncomeModel.class),
                    generateUID(GdMsgModel.class), realm.createObject(GdMsgModel.class), "Желательно к среде", DateFormatHelper.getDate(Calendar.DATE, -33), dateFormatter.parse("01.08.2014"), dateFormatter.parse("01.12.2014"));
            //17
            GdMsgModel gdMsgEmployeeModel = ModelFactory.newInstanceGdMsgEmployeeModel(generateUID(GdMsgEmployeeModel.class), realm.createObject(GdMsgEmployeeModel.class),
                    generateUID(GdMsgModel.class), realm.createObject(GdMsgModel.class), "", DateFormatHelper.getDate(Calendar.DATE, -32), employeeModel);
            //18
            GdMsgModel gdMsgHolidayModel2 = ModelFactory.newInstanceGdMsgHolidayModel(generateUID(GdMsgHolidayModel.class), realm.createObject(GdMsgHolidayModel.class),
                    generateUID(GdMsgModel.class), realm.createObject(GdMsgModel.class), "", DateFormatHelper.getDate(Calendar.DATE, -32), dateTimeFormatter.parse("11.08.2015 00:00"), dateFormatter.parse("03.09.2015 00:00"), GdMsgHolidayModel.Type.DEFAULT);

            ModelFactory.newInstanceGdMsgEmployeeModel(generateUID(GdMsgEmployeeModel.class), realm.createObject(GdMsgEmployeeModel.class),
                    generateUID(GdMsgModel.class), realm.createObject(GdMsgModel.class), "", "", DateFormatHelper.getDate(Calendar.DATE, -31),
                    GdMsgModel.RequestStatus.SUCCESS, gdMsgEmployeeModel);

            ModelFactory.newInstanceGdMsgHolidayModel(generateUID(GdMsgHolidayModel.class), realm.createObject(GdMsgHolidayModel.class),
                    generateUID(GdMsgModel.class), realm.createObject(GdMsgModel.class), "123456", "", DateFormatHelper.getDate(Calendar.DATE, -31),
                    GdMsgModel.RequestStatus.SUCCESS, gdMsgHolidayModel2);

            ModelFactory.newInstanceGdMsgIncomeModel(generateUID(GdMsgIncomeModel.class), realm.createObject(GdMsgIncomeModel.class),
                    generateUID(GdMsgModel.class), realm.createObject(GdMsgModel.class), "", "Можете забрать после 12:00", DateFormatHelper.getDate(Calendar.DATE, -30),
                    GdMsgModel.RequestStatus.SUCCESS, gdMsgIncomeModel);

            ModelFactory.newInstanceWorkScheduleGdMsgModel(generateUID(GdMsgWorkScheduleModel.class), realm.createObject(GdMsgWorkScheduleModel.class),
                    generateUID(GdMsgModel.class), realm.createObject(GdMsgModel.class), "Суббота рабочая", "", DateFormatHelper.getDate(Calendar.DATE, -1),
                    GdMsgModel.RequestStatus.UNKNOWN);
            //23
        } catch (ParseException ignored) {
        }

        realm.commitTransaction();
    }

    private void generateInstanseWorkSchedules(Date dateBegin, Date dateEnd, EmployeeModel employeeModel, List<Date> holidayDates, List<Date> transfersWorkDay, List<String> holidayNames) { //TODO final  params
        SimpleDateFormat dateFormatter = new SimpleDateFormat("dd.MM.yyyy");
        SimpleDateFormat dateTimeFormatter = new SimpleDateFormat("dd.MM.yyyy HH:mm");
        SimpleDateFormat dateWeekDayFormatter = new SimpleDateFormat("EE");

        Calendar beginCal = Calendar.getInstance();
        beginCal.setTime(dateBegin);

        Calendar endCal = Calendar.getInstance();
        endCal.setTime(dateEnd);

        boolean isDayShift = true;
        while (!beginCal.after(endCal)) {
            Date day = beginCal.getTime();

            if (holidayDates.contains(day)) { //TODO locale

                WorkScheduleModel daySchedule = ModelFactory.newInstanseWorkScheduleModel(
                        realm.createObject(WorkScheduleModel.class),
                        WorkScheduleModel.DayType.DAY_OFF,
                        employeeModel,
                        day);

                daySchedule.getEvents().add(ModelFactory.newInstanceDayEventModel(
                        realm.createObject(DayEventModel.class),
                        DayEventModel.EventType.PUBLIC_HOLIDAY,
                        holidayNames.get(holidayDates.indexOf(day)), //TODO название праздника
                        true));
            } else if ((dateWeekDayFormatter.format(day).equals("сб") && !transfersWorkDay.contains(day) && !holidayDates.contains(day)) || (dateWeekDayFormatter.format(day).equals("вс") && !transfersWorkDay.contains(day)) && !holidayDates.contains(day)) {
                ModelFactory.newInstanseWorkScheduleModel(
                        realm.createObject(WorkScheduleModel.class),
                        WorkScheduleModel.DayType.DAY_OFF,
                        employeeModel,
                        day);
            } else if ((!dateWeekDayFormatter.format(day).equals("сб") && !dateWeekDayFormatter.format(day).equals("вс") && !holidayDates.contains(day)) || transfersWorkDay.contains(day)) {
                WorkScheduleModel daySchedule = ModelFactory.newInstanseWorkScheduleModel(
                        realm.createObject(WorkScheduleModel.class),
                        WorkScheduleModel.DayType.WORK,
                        employeeModel,
                        day);
                try {
                    if (transfersWorkDay.contains(day)) { // TODO доделать
                        daySchedule.getEvents().add(ModelFactory.newInstanceDayEventModel(
                                realm.createObject(DayEventModel.class),
                                DayEventModel.EventType.CHANGE_WORK_SCHEDULE,
                                "Перенос рабочего дня на выходной.",
                                true));
                    }

                    if (isDayShift) {
                        daySchedule.getEvents().add(ModelFactory.newInstanceDayEventModel(
                                realm.createObject(DayEventModel.class),
                                DayEventModel.EventType.DAY_SHIFT,
                                dateTimeFormatter.parse(dateFormatter.format(day) + " 09:00"),
                                dateTimeFormatter.parse(dateFormatter.format(day) + " 17:00"),
                                "",
                                false));
                    } else {
                        daySchedule.getEvents().add(ModelFactory.newInstanceDayEventModel(
                                realm.createObject(DayEventModel.class),
                                DayEventModel.EventType.NIGHT_SHIFT,
                                dateTimeFormatter.parse(dateFormatter.format(day) + " 17:00"),
                                dateTimeFormatter.parse(dateFormatter.format(day) + " 24:00"),
                                "",
                                false));
                    }

                } catch (ParseException ignore) {
                }

                isDayShift = !isDayShift;
            }

            beginCal.add(Calendar.DATE, 1);
        }
    }

}
